#include "POSIX_Time.h"

struct timespec time_hou, time_qian;

//************************************
// Method:    	TimeSig_Deal		定时器超时后中断处理函数
// Programmer:  lww
// Time:		2018/12/13
// Returns:   	void
// Parameter: 	int sign			信号名称
// Parameter: 	siginfo_t * info	中断处理函数传入参数
// Parameter: 	void *
//************************************
void TimeSig_Deal(int sign, siginfo_t* info, void*)
{
	switch (info->si_value.sival_int)
	{
	case BcDPUSIG:	
		/*MUTEX_UNLOCK(BcDPU_Mutex, "BcDPU_Mutex unlock failed");*/				//< 主站定时广播DPU状态信号量
		SEM_POST(BcDPUSemId, "BcDPUSemId give failed"); //释放信号量
		break;
	case BcNetIOSIG:
		/*MUTEX_UNLOCK(BcNetIO_Mutex, "BcNetIO_Mutex unlock failed");*/			//< 主站定时广播站间通讯模块DO、AO数据信号量
		SEM_POST(BcNetIOSemId, "BcNetIOSemId give failed");
		break;
	case CalcuSIG:
		//time_hou = RealTime_Get();
		//printf("dt_SIG = %d\r\n", Dt_Calu(time_hou, time_qian));
		//time_qian = time_hou;
		/*MUTEX_UNLOCK(Calcu_Mutex, "Calcu_Mutex unlock failed");*/				//< 各运算周期定时运算信号量
		SEM_POST(CalcuSemId, "CalcuSemId give failed");
		break;
	case BcAVIOSIG:
		/*MUTEX_UNLOCK(BcAVIO_Mutex, "BcAVIO_Mutex unlock failed");*/			//< 主站定时广播模拟量硬点信号量
		SEM_POST(BcAVIOSemId, "BcAVIOSemId give failed");
		break;
	case BcDVIOSIG:
		/*MUTEX_UNLOCK(BcDVIO_Mutex, "BcDVIO_Mutex unlock failed");*/			//< 主站定时广播数字量硬点信号量
		SEM_POST(BcDVIOSemId, "BcDVIOSemId give failed");
		break;
	case BcSamaIOSIG:
		/*MUTEX_UNLOCK(BcSamaIO_Mutex, "BcSamaIO_Mutex unlock failed");*/		//< 主站定时广播SAMA软点信号量
		SEM_POST(BcSamaIOSemId, "BcSamaIOSemId give failed");
		break;
	case ReadSamaIOSIG:
		/*MUTEX_UNLOCK(ReadSamaIO_Mutex, "ReadSamaIO_Mutex unlock failed");*/	//< 定时读取软点数据
		SEM_POST(ReadSamaIOSemId, "ReadSamaIOSemId give failed");
		break;
	case BcSamaSIG:
		/*MUTEX_UNLOCK(BcSama_Mutex, "BcSama_Mutex unlock failed");*/			//< 主站定时广播SAMA参数信号量
		SEM_POST(BcSamaSemId, "BcSamaSemId give failed");
		break;
	case BcSamaParaSIG:
		/*MUTEX_UNLOCK(BcSamaPara_Mutex, "BcSamaPara_Mutex unlock failed");*/	//< 主站定时广播SAMA模块参数信号量
		SEM_POST(BcSamaParaSemId, "BcSamaParaSemId give failed");
		break;
	case BakDataSIG:
		/*MUTEX_UNLOCK(BakData_Mutex, "BakData_Mutex unlock failed");*/			//< 主站定时向从站备份信号量
		SEM_POST(BakDataSemId, "BakDataSemId give failed");
		break;
	case BakIOSIG:
		/*MUTEX_UNLOCK(BakIO_Mutex, "BakIO_Mutex unlock failed");*/				//< 主站向从站备份IO硬点数据信号量
		SEM_POST(BakIOSemId, "BakIOSemId give failed");
		break;
	case TestDCSTimeOutSIG:
		//< 这里这么写保证TimeOut_Deal只执行一次
		DCS_TimeOut_Count++;
		if (DCS_TimeOut_Count >= DCS_TIMEOUT_MAXNUM)
		{
			if (DCS_TimeOut_Count == DCS_TIMEOUT_MAXNUM)
				TimeOut_Deal();
			DCS_TimeOut_Count = DCS_TIMEOUT_MAXNUM + 1;
		}
		//printf("DCS_TimeOut_Count = %d\r\n", DCS_TimeOut_Count);
		break;
	//case IEC104CallAllSIG:																	
	//	MUTEX_UNLOCK(IEC104_CallAll_Mutex, "IEC104_CallAll_Mutex unlock failed");				//< IEC104主站通讯定时发送总召唤信号量	
	//	break;
	//case IEC104ClockSIG:
	//	MUTEX_UNLOCK(IEC104_Clock_Mutex, "IEC104_Clock_Mutex unlock failed");					//< IEC104主站通讯定时发送对时信号量			
	//	break;
	}
}


//************************************
// Method:    	Timer_Create				创建定时器函数
// Programmer:  lww
// Time:		2018/12/13
// Returns:   	bool
// Parameter: 	timer_t * timer_name		定时器ID句柄
// Parameter: 	int label					定时器标签号，标志是哪个定时器
// Parameter: 	struct timespec * time		定时器时间值
//************************************
bool Timer_Create(timer_t* timer_name, int label, struct timespec* time)
{
	//////< POSIX 定时器属性设置结构体，结构体构成如下
	//////		typedef struct sigevent {
	//////		sigval_t sigev_value;
	//////		int sigev_signo;
	//////		int sigev_notify;
	//////		union {
	//////			int _pad[SIGEV_PAD_SIZE];
	//////			 int _tid;

	//////			struct {
	//////				void (*_function)(sigval_t);
	//////				void *_attribute;    /* really pthread_attr_t */
	//////				} _sigev_thread;
	//////			} _sigev_un;
	//////		} sigevent_t;
	struct sigevent evp;	

	struct itimerspec ts;	//< 定义定时器的时间结构体
	struct sigaction act;	//< 定时信号处理方式结构体

#define SIG_NAME SIGUSR1
	//< POSIX定时器设置
	memset(&evp, 0, sizeof(struct sigevent));	//< 初始化POSIX定时器属性设置结构体

	/* 
	定义当定时器超期后如何通过该进程
	1)SIGEV_NONE		不需要异步通知，程序自己调用timer_gettime来轮询timer的当前状态
	2)SIGEV_SIGNAL		使用sinal这样的异步通知方式。发送的信号由sigev_signo定义。如果发送的是realtime signal，该信号的附加数据由sigev_value定义
	3)SIGEV_THREAD		创建一个线程执行timer超期callback函数，_attribute定义了该线程的属性
	4)SIGEV_THREAD_ID	行为和SIGEV_SIGNAL类似，不过发送的信号被送达进程内的一个指定的thread，这个thread由_tid标识
	2)/3)/4)都是通过发送信号这种异步通知方式；只不过3)将callback函数放在一个单独线程中进行处理；4)是通过创建的线程来接收信号通知
	在本项目中采用的是第2)种方式，通过超时后发送自定义信号的方式，通过附加数据sigev_value判断是哪个定时器超时了，在TimeSig_Deal函数中根据附件数据判断执行哪套中断处理程序
	*/
	evp.sigev_notify = SIGEV_SIGNAL;
	evp.sigev_signo = SIG_NAME;			//< 定时定时器超时后发送的信号的名称
	evp.sigev_value.sival_int = label;	//< 被发送信号的附加数据值，每个定时器的附加数据值是不同的，用于区别是哪一个定时器到期了，从而执行不同的中断服务，sigev_value的值将被作为siginfo_t结构体中si_value的值

	//< 信号量接收函数设置
	memset(&act, 0, sizeof(struct sigaction));	//< 初始化信号处理方式结构体

	//////< 信号新处理方式设置结构体
	//////		struct sigaction {
	//////			union {
	//////				__sighandler_t _sa_handler;
	//////				void(*_sa_sigaction)(int, struct siginfo *, void *)；
	//////			}_u
	//////				sigset_t sa_mask；
	//////					unsigned long sa_flags；
	//////					void(*sa_restorer)(void)；
	//////		}
	/*
	当sa_flags成员的值包含了SA_SIGINFO标志时，系统将使用sa_sigaction函数作为信号处理函数，否则使用sa_handler作为信号处理函数
	sa_mask成员用来指定在信号处理函数执行期间需要被屏蔽的信号
	sa_flags成员用于指定信号处理的行为，它可以是以下值“按位或”组合
		SA_RESTART:使被信号打断的系统调用自动重新发起
		SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到SIGCHLD信号
		SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到SIGCHLD信号，这时子进程如果退出也不会成为僵尸进程。
		SA_NODEFER:使对信号的屏蔽无效，即在信号处理函数执行期间仍能发生这个信号
		SA_REETHAND:信号处理之后重新设置为默认的处理方式
		SA_SIGINFO:使用sa_sigaction成员而不是sa_handler作为信号处理函数
	*/
	act.sa_flags = SA_SIGINFO;
	act.sa_sigaction = TimeSig_Deal; //定时器中断处理函数

	/*
	sigaction是POSIX标准定义的信号处理接口
	参数一：要操作的信号
	参数二：要设置的对信号的新处理方式
	参数三：原来对信号的处理方式
	*/
	if (sigaction(SIG_NAME, &act, NULL) < 0)
		return false;
#undef SIG_NAME

	/*
	创建定时器
	参数一：clockid，识别系统时钟的id号，此处用CLOCK_REALTIME，表示真实世界的时钟
		CLOCK_REALTIME :Systemwide realtime clock.	全系统实时时钟
		CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.	代表单调时间。无法设定。
		CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.	高分辨率每进程计时器。
		CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.	特定于线程的计时器。
		CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.	CLOCK_REALTIME的高分辨率版本。
		CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.	CLOCK_MONOTONIC的高分辨率版本。
	参数二：sigevent，定时器的属性，上文程序中已经进行过配置
	参数三：timerid，返回定时器的ID句柄
	*/
	if ((timer_create(CLOCK_REALTIME, &evp, timer_name)) != 0)
	{
		printf("Timer create failed in %s\r\n", STR_NAME(*timer_name));
		return false;
	}

	/*
	it_value用于指定当前的定时器到期时间
	当定时器到期，
	it_value的值会被更新成it_interval的值
	如果it_interval的值为0，则定时器不是一个时间间隔定时器，一旦it_value到期就会回到未启动状态
	本项目中，因为我们用到的是时间间隔定时器，所以此处it_value与it_interval的值设置为相同时间
	*/
	ts.it_interval.tv_sec = time->tv_sec;
	ts.it_interval.tv_nsec = time->tv_nsec;
	ts.it_value.tv_sec = time->tv_sec;
	ts.it_value.tv_nsec = time->tv_nsec;

	/*
	timer_create()所创建的定时器并未启动,要通过timer_settime()将它关联到一个到期时间以及启动时钟周期
	参数一：定时器ID句柄
	参数二：如果flags的值为TIMER_ABSTIME，则第三个参数所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)
	参数三：定时器所指定的时间
	参数四：如果该参数的值不是NULL，则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态，则此结构的成员全都会被设定成0
	*/
	if ((timer_settime(*timer_name, CLOCK_REALTIME, &ts, NULL)) != 0)
	{
		printf("Timer set time failed in %s\r\n", STR_NAME(*timer_name));
		timer_delete(*timer_name);
		return false;
	}

	return true;
}


//************************************
// Method:    	MS2TimeSpec					将MS时间转化为Linux内部时间变量timespec
// Programmer:  lww
// Time:		2018/12/26
// Returns:   	void
// Parameter: 	struct timespec * ts		Linux内部时间变量指针
// Parameter: 	int ms						MS时间值
//************************************
void MS2TimeSpec(struct timespec* ts, int ms)
{
	int _s, _ms;
	_s = ms / 1000;
	_ms = ms % 1000;
	ts[0].tv_sec = _s;
	ts[0].tv_nsec = MS2NS(_ms);
}


//************************************
// Method:    	MS2Timeval					将MS时间转化为Linux内部时间变量Timeval
// Programmer:  lww
// Time:		2019/2/16
// Returns:   	void
// Parameter: 	struct timeval * ts			Linux内部时间变量指针
// Parameter: 	int ms						MS时间值
//************************************
void MS2Timeval(struct timeval* ts, int ms)
{
	int _s, _ms;
	_s = ms / 1000;
	_ms = ms % 1000;
	ts[0].tv_sec = _s;
	ts[0].tv_usec = MS2US(_ms);
}


//************************************
// Method:    	RealTime_Get				系统当前时间获取
// Programmer:  lww
// Time:		2019/4/15
// Returns:   	struct timespec
//************************************
struct timespec RealTime_Get()
{
	struct timespec ts = { 0 };
	if (clock_gettime(CLOCK_REALTIME, &ts) == -1)		//< duan0603 CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC（世界标准时间）1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他, 则对应的时间相应改变
	{
		perror("RealTime_Get failed\n");
	}
	//< duan0608 上位接收 ts 后，经处理，报警时间比实际时间多8个小时，特将下述代码注释。
	//< ts.tv_sec += 28800;						       //< 由于获取到的是格林尼治时间，需加上8*3600=28800s变为北京时间
	return ts;
}

//************************************
// Method:    	RealTime_Set				设置系统时间
// Programmer:  lww
// Time:		2019/4/15
// Returns:   	struct timespec
//************************************
bool RealTime_Set(struct timespec ts)
{
	if (clock_settime(CLOCK_REALTIME, &ts) == -1)		//< duan0603 CLOCK_REALTIME系统实时时间
	{
		perror("RealTime_Set failed\n");
		return false;
	}
	//< duan0608 上位接收 ts 后，经处理，报警时间比实际时间多8个小时，特将下述代码注释。
	//< ts.tv_sec += 28800;						       //< 由于获取到的是格林尼治时间，需加上8*3600=28800s变为北京时间
	return true;
}


//************************************
// Method:    	Dt_Calu							时间差获取（单位：ms）
// Programmer:  lww
// Time:		2019/2/22
// Returns:   	int
// Parameter: 	struct timespec ts1				节点1时间
// Parameter: 	struct timespec ts2				节点2时间
//************************************
int Dt_Calu(struct timespec ts1, struct timespec ts2)
{
	//< duan0607 1s(秒)= 10的三次方ms(毫秒) = 10的六次方us(微妙) = 10的九次方ns(纳秒)
	int dt_ms = 0;
	long ts1_s, ts1_ns, ts2_s, ts2_ns;

	ts1_s = (long)ts1.tv_sec;
	ts2_s = (long)ts2.tv_sec;
	ts1_ns = (long)ts1.tv_nsec;
	ts2_ns = (long)ts2.tv_nsec;

	dt_ms = (int)((ts1_s * 1000 + ts1_ns / 1000000) - (ts2_s * 1000 + ts2_ns / 1000000));
	return dt_ms;
}


//************************************
// Method:    	RoughDelay_S			延时固定秒数（粗糙）
// Programmer:  lww
// Time:		2019/3/24
// Returns:   	void
// Parameter: 	int second				秒
//************************************
void RoughDelay_S(int second)
{
	//< 因为sleep会被其他信号打断，因此此处这种写法可以保证sleep的延时时间，sleep的返回值是被打断时剩余的时间
	do {
		second = sleep(second);
	} while (second > 0);
}


//************************************
// Method:    	ExactDelay				延时固定毫秒数（精细）
// Programmer:  lww
// Time:		2019/3/24
// Returns:   	void
// Parameter: 	int millisecond			毫秒
//************************************
void ExactDelay(int millisecond)
{
	struct timeval tv;
	int ret;
	MS2Timeval(&tv, millisecond);
	do {
		ret = select(0, NULL, NULL, NULL, &tv);			//< tv参数为超时时间，该结构会被内核修改，其值为超时剩余的时间
	} while (ret < 0 && errno == EINTR);
	
}
